package cn.geminis.workflow.service.controller;

import cn.geminis.auth.context.LoginUser;
import cn.geminis.data.jpa.PageQuery;
import cn.geminis.workflow.service.service.engine.expression.ExpressionValueContext;
import cn.geminis.workflow.service.utils.ProcessQueryUtils;
import cn.geminis.workflow.service.utils.QueryUtils;
import cn.geminis.workflow.service.utils.SpringUtils;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ProcessEngine;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

/**
 * @author puddi
 */
@RestController
@RequestMapping("/process")
@AllArgsConstructor
public class ProcessController {

    private final ProcessEngine engine;

    @GetMapping("/my")
    public Page<Process> myProcesses(final PageQuery pageParameter,
                                     String category) {

        var query = engine.getRepositoryService()
                .createProcessDefinitionQuery()
                .latestVersion()
                .startableByUser(LoginUser.fullName());

        ProcessQueryUtils.filterCategory(query, category);
        query.orderByProcessDefinitionName().asc();

        return QueryUtils.toPage(query, pageParameter, Process.class);
    }

    @GetMapping("/{processKey}/startFormUrl")
    public String startFormUrl(@PathVariable String processKey) {
        var processDefinition = engine.getRepositoryService().createProcessDefinitionQuery()
                .processDefinitionKey(processKey)
                .latestVersion()
                .singleResult();
        var model = engine.getRepositoryService().getBpmnModel(processDefinition.getId());
        var startEvent = (StartEvent) model.getMainProcess().getInitialFlowElement();
        var firstTask = (UserTask) startEvent.getOutgoingFlows().get(0).getTargetFlowElement();
        return firstTask.getFormKey();
    }

    @PostMapping(path = {"/{key}/start/{businessKey}"})
    public String start(@PathVariable String key,
                        @PathVariable(required = false) Optional<String> businessKey,
                        @RequestBody(required = false) Map<String, Object> data) {
        var bizKey = businessKey.orElse(UUID.randomUUID().toString());
        var username = LoginUser.fullName();

        engine.getIdentityService().setAuthenticatedUserId(username);

        var valueContext = SpringUtils.getBean(ExpressionValueContext.class);
        valueContext.clear();
        valueContext.withData(data);

        var instance = engine.getRuntimeService().startProcessInstanceByKey(key, bizKey);

        var taskService = engine.getTaskService();
        var firstTask = taskService.createTaskQuery().processInstanceId(instance.getId()).list().get(0);
        taskService.setAssignee(firstTask.getId(), username);
        taskService.complete(firstTask.getId());

        return bizKey;
    }

    @DeleteMapping("/stop")
    public void stop(String[] processIds, String reason) {
        Stream.of(processIds).forEach(processId -> this.engine.getRuntimeService()
                .deleteProcessInstance(processId, reason));
    }

    @DeleteMapping("/stop/{processKey}/{businessKey}")
    public void stopByBusinessKey(@PathVariable String processKey, @PathVariable String businessKey) {
        var instance = this.engine.getRuntimeService()
                .createProcessInstanceQuery()
                .processDefinitionKey(processKey)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (!Objects.isNull(instance)) {
            this.engine.getRuntimeService().deleteProcessInstance(instance.getId(), null);
        }
    }
}
